1 module hunt.templates.parser;
2 
3 import std.string;
4 import std.file;
5 import std.path;
6 import std.conv;
7 import std.stdio;
8 
9 import hunt.templates.rule;
10 import hunt.templates.element;
11 import hunt.templates.match;
12 import hunt.templates.ast;
13 import hunt.templates.util;
14 import hunt.templates.cache;
15 
16 class Parser
17 {
18 public:
19     ElementNotation element_notation = ElementNotation.Pointer;
20 
21     this()
22     {
23     }
24 
25     ElementExpression parse_expression(const string input)
26     {
27 
28         if (input.length <= 0)
29             return new ElementExpression(Function.ReadJson);
30         auto match_function = RegexObj.match!Function(input, regex_map_functions);
31         switch (match_function.type())
32         {
33         case Function.ReadJson:
34             {
35                 string command = match_function.str(1);
36 
37                 if ((startsWith(command, '"') && endsWith(command, '"'))
38                         || (startsWith(command, '\'') && endsWith(command, '\'')))
39                 { //  Result
40                     ElementExpression result = new ElementExpression(Function.Result);
41                     result.result = command[1 .. $ - 1];
42                     return result;
43                 }
44 
45                 ElementExpression result = new ElementExpression(Function.ReadJson);
46                 switch (element_notation)
47                 {
48                 case ElementNotation.Pointer:
49                     {
50                         //if (command[0] != '/') { command = "/"~command; }
51                         result.command = command;
52                         break;
53                     }
54                 case ElementNotation.Dot:
55                     {
56                         result.command = command;
57                         break;
58                     }
59                 default:
60                     template_engine_throw("parser_error",
61                             "element notation: " ~ element_notation.stringof);
62                     break;
63                 }
64                 return result;
65             }
66         default:
67             {
68                 ElementExpression[] args;
69                 for (int i = 1; i < match_function.size(); i++)
70                 { // str(0) is whole group
71                     args ~= parse_expression(match_function.str(i));
72                 }
73 
74                 ElementExpression result = new ElementExpression(match_function.type());
75                 result.args = args;
76                 return result;
77             }
78         }
79     }
80 
81     Element[] parse_level(string input, string path)
82     {
83         Element[] result;
84 
85         size_t current_position = 0;
86         auto match_delimiter = RegexObj.search_all(input, current_position);
87         //writeln("-----3------");
88         while (match_delimiter.found())
89         {
90             current_position = match_delimiter.end_position();
91             //writeln("---whole --- :",match_delimiter.str());
92             string string_prefix = match_delimiter.prefix();
93             if (!string_prefix.empty())
94             {
95                 result ~= new ElementString(string_prefix);
96             }
97 
98             string delimiter_inner = match_delimiter.str(1);
99 
100             switch (match_delimiter.type())
101             {
102             case Delimiter.Statement:
103             case Delimiter.LineStatement:
104                 {
105 
106                     auto match_statement = RegexObj.match!Statement(delimiter_inner,
107                             regex_map_statement_openers);
108                     switch (match_statement.type())
109                     {
110                     case Statement.Loop:
111                         {
112                             MatchClosed loop_match = RegexObj.search_closed(input,
113                                     match_delimiter.pattern(), regex_map_statement_openers[Statement.Loop],
114                                     regex_map_statement_closers[Statement.Loop], match_delimiter);
115 
116                             current_position = loop_match.end_position();
117 
118                             string loop_inner = match_statement.str(0);
119                             auto match_command = RegexObj.match!Loop(loop_inner, regex_map_loop);
120                             if (!match_command.found())
121                             {
122                                 template_engine_throw("parser_error",
123                                         "unknown loop statement: " ~ loop_inner);
124                             }
125                             //writeln("#############match type :",match_command.type());
126                             switch (match_command.type())
127                             {
128                             case Loop.ForListIn:
129                                 {
130                                     string value_name = match_command.str(1);
131                                     string list_name = match_command.str(2);
132                                     result ~= new ElementLoop(match_command.type(), value_name,
133                                             parse_expression(list_name), loop_match.inner());
134                                     break;
135                                 }
136                             case Loop.ForMapIn:
137                                 {
138                                     string key_name = match_command.str(1);
139                                     string value_name = match_command.str(2);
140                                     string list_name = match_command.str(3);
141 
142                                     result ~= new ElementLoop(match_command.type(), key_name, value_name,
143                                             parse_expression(list_name), loop_match.inner());
144                                     break;
145                                 }
146                             default:
147                                 template_engine_throw("parser_error",
148                                         "unknown loop statement: " ~ match_command.str());
149                                 break;
150                             }
151                             break;
152                         }
153                     case Statement.Condition:
154                         {
155                             auto condition_container = new ElementConditionContainer();
156 
157                             Match condition_match = match_delimiter;
158                             MatchClosed else_if_match = RegexObj.search_closed_on_level(input,
159                                     match_delimiter.pattern(),
160                                     regex_map_statement_openers[Statement.Condition],
161                                     regex_map_statement_closers[Statement.Condition],
162                                     regex_map_condition[Condition.ElseIf], condition_match);
163                             while (else_if_match.found())
164                             {
165                                 condition_match = else_if_match._close_match;
166 
167                                 string else_if_match_inner = else_if_match._open_match.str(1);
168                                 auto match_command = RegexObj.match!Condition(else_if_match_inner,
169                                         regex_map_condition);
170                                 if (!match_command.found())
171                                 {
172                                     template_engine_throw("parser_error",
173                                             "unknown if statement: " ~ else_if_match._open_match.str());
174                                 }
175                                 condition_container.children ~= new ElementConditionBranch(else_if_match.inner(),
176                                         match_command.type(),
177                                         parse_expression(match_command.str(1)));
178 
179                                 else_if_match = RegexObj.search_closed_on_level(input, match_delimiter.pattern(),
180                                         regex_map_statement_openers[Statement.Condition],
181                                         regex_map_statement_closers[Statement.Condition],
182                                         regex_map_condition[Condition.ElseIf], condition_match);
183                             }
184 
185                             MatchClosed else_match = RegexObj.search_closed_on_level(input, match_delimiter.pattern(),
186                                     regex_map_statement_openers[Statement.Condition],
187                                     regex_map_statement_closers[Statement.Condition],
188                                     regex_map_condition[Condition.Else], condition_match);
189                             if (else_match.found())
190                             {
191                                 condition_match = else_match._close_match;
192 
193                                 string else_match_inner = else_match._open_match.str(1);
194                                 auto match_command = RegexObj.match!Condition(else_match_inner,
195                                         regex_map_condition);
196                                 if (!match_command.found())
197                                 {
198                                     template_engine_throw("parser_error",
199                                             "unknown if statement: " ~ else_match._open_match.str());
200                                 }
201                                 //writeln("################### :",match_command.str(1),"   else match inner : ",else_match_inner);
202                                 condition_container.children ~= new ElementConditionBranch(else_match.inner(),
203                                         match_command.type(),
204                                         parse_expression(match_command.str(1)));
205                             }
206 
207                             MatchClosed last_if_match = RegexObj.search_closed(input, match_delimiter.pattern(),
208                                     regex_map_statement_openers[Statement.Condition],
209                                     regex_map_statement_closers[Statement.Condition],
210                                     condition_match);
211                             //MatchClosed last_if_match = RegexObj.search_closed_on_level(input, match_delimiter.pattern(), regex_map_statement_openers[Statement.Condition], regex_map_statement_closers[Statement.Condition], regex_map_statement_closers[Statement.Condition], condition_match);
212                             if (!last_if_match.found())
213                             {
214                                 writeln("--####- - : ",delimiter_inner);
215                                 template_engine_throw("parser_error", "misordered if statement");
216                             }
217 
218                             string last_if_match_inner = last_if_match._open_match.str(1);
219                             auto match_command = RegexObj.match!Condition(last_if_match_inner,
220                                     regex_map_condition);
221                             if (!match_command.found())
222                             {
223                                 template_engine_throw("parser_error",
224                                         "unknown if statement: " ~ last_if_match._open_match.str());
225                             }
226                             if (match_command.type() == Condition.Else)
227                             {
228                                 //writeln("################### 1:",last_if_match.inner());
229                                 condition_container.children ~= new ElementConditionBranch(last_if_match.inner(),
230                                         match_command.type());
231                             }
232                             else
233                             {
234                                 //writeln("################### 2:",match_command.str(1));
235                                 condition_container.children ~= new ElementConditionBranch(last_if_match.inner(),
236                                         match_command.type(),
237                                         parse_expression(match_command.str(1)));
238                             }
239 
240                             current_position = last_if_match.end_position();
241                             result ~= condition_container;
242                             break;
243                         }
244                     case Statement.Include:
245                         {
246                             string included_filename = path ~ match_statement.str(1);
247                             ASTNode included_template = parse_template(included_filename);
248                             foreach (element; included_template.parsed_node.children)
249                             {
250                                 result ~= element;
251                             }
252                             break;
253                         }
254                     default:
255                         {
256                             template_engine_throw("parser_error",
257                                     "unknown  statement: " ~ to!string(match_statement.type()));
258                             break;
259                         }
260                     }
261                     break;
262                 }
263             case Delimiter.Expression:
264                 {
265                     result ~= parse_expression(delimiter_inner);
266                     break;
267                 }
268             case Delimiter.Comment:
269                 {
270                     result ~= new ElementComment(delimiter_inner);
271                     break;
272                 }
273             default:
274                 {
275                     template_engine_throw("parser_error",
276                             "unknown  statement: " ~ to!string(match_delimiter.type()));
277                     break;
278                 }
279             }
280 
281             match_delimiter = RegexObj.search_all(input, current_position);
282             //writeln("-----4------: ",current_position);
283         }
284         if (current_position < input.length)
285         {
286             result ~= new ElementString(input[current_position .. $]);
287         }
288 
289         return result;
290     }
291 
292     Element parse_tree(Element current_element, string path)
293     {
294 
295         if (current_element.inner.length > 0)
296         {
297             //writeln("-----parse_level ------ : ", current_element.inner);
298             current_element.children = parse_level(current_element.inner, path);
299             current_element.inner = string.init;
300         }
301 
302         if (current_element.children.length > 0)
303         {
304             for (size_t i = 0; i < current_element.children.length; i++)
305             {
306                 //writeln("-----2------");
307                 auto em = current_element.children[i];
308                 //writeln("*******type : ",current_element.type);
309                 current_element.children[i] = parse_tree(em, path);
310             }
311         }
312         return current_element;
313     }
314 
315     ASTNode parse(string input)
316     {
317         auto parsed = parse_tree(new Element(Type.Main, input), "./");
318         return new ASTNode(parsed);
319     }
320 
321     ASTNode parse_template(string filename)
322     {
323         auto node = ASTCache.node(filename);
324         if (node !is null)
325             return node;
326 
327         string input = load_file(filename);
328         string path = dirName(filename);
329         //writeln("----template file path : ",filename);
330         auto parsed = parse_tree(new Element(Type.Main, input), path ~ "/");
331         auto astnode = new ASTNode(parsed);
332         ASTCache.add(filename, astnode);
333         return astnode;
334     }
335 
336     string load_file(string filename)
337     {
338         return cast(string) std.file.read(filename);
339     }
340 }